#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__

#include "log.hpp"
#include <string.h>
#include <string>
#include <memory>
#include "InetAddr.hpp"
#include <sys/socket.h>
#include <sys/types.h>
#include <functional>
#include <netinet/in.h>
#include <arpa/inet.h>

using find_t =std::function<std::string(const std::string &)>;

using namespace LogModule;

#define Die(code)   \
    do              \
    {               \
        exit(code); \
    } while (0)

static int defaultfd = -1;
static std::string defaultip = "127.0.0.1"; // 默认的IP地址，代表本地IP地址
static uint16_t defaultport = 8080;         // 默认的端口号,用来测试

namespace UdpServerModule
{

    class UdpServer
    {
    public:
        UdpServer(const std::string &ip = defaultip, const uint16_t port = defaultport,find_t findword = \
            [](const std::string &word) { return "Translate isn't init successfully " ; })
            : _sockfd(defaultfd),
              local(port), // 初始化本地地址信息
              is_running(false),
              _findword(findword)
        {
        }
        ~UdpServer()
        {
        }

        void InitServer()
        {
            // 1.创建一个socket
            _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
            // 这里我们使用了C标准库的socket函数来创建一个UDP socket
            // 注意：在实际的代码中，我们需要检查socket函数的返回值，以确保
            // socket创建成功。

            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "Failed to create socket" << strerror(errno);
                // 如果socket创建失败，我们记录一条FATAL级别的日志，并返回。
                Die(1);
            }

            // 如果socket创建成功，我们记录一条INFO级别的日志，表示socket创建成功。
            LOG(LogLevel::INFO) << "Socket created successfully, sockfd: " << _sockfd;

            // 2.绑定地址信息
            // struct sockaddr_in local;
            // bzero(&local, sizeof(local)); // 清空结构体
            // // 这里我们使用了bzero函数来清空local结构体，确保没有残留数据,垃圾值
            // local.sin_family = AF_INET;    // 设置地址族为IPv4
            // local.sin_port = htons(_port); // 设置端口号
            // // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 将IP地址转换为网络字节序
            // local.sin_addr.s_addr = INADDR_ANY; // 绑定到任意IP地址，这样服务器可以接收来自任何IP的消息

            int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n < 0)
            {
                // 如果bind函数返回小于0，表示绑定失败，我们记录一条FATAL级别的日志，并返回。
                // 这里我们使用了strerror函数来获取错误信息，并将其记录到日志中。
                LOG(LogLevel::FATAL) << "bind: " << strerror(errno);
                Die(2);
            }
            // 如果绑定成功，我们记录一条INFO级别的日志，表示绑定成功。
            LOG(LogLevel::INFO) << "bind success ";
        }
        void Start()
        {
            is_running = true;

            while (is_running)
            {

                char buffer[1024];
                struct sockaddr_in peer;      // 输出型参数
                socklen_t len = sizeof(peer); // 也是一个输出型参数
                ssize_t n = ::recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);
                if (n > 0)
                {

                    InetAddr temp(peer); // 通过上面的peer来进行初始化，这样以来我们就能获取到相关ip地址与端口并打印
                    buffer[n] = '\0';    // 确保字符串以null结尾
                    // LOG(LogLevel::INFO) << "client ip: " << temp.GetIp() << ", port: " << temp.GetPort()
                    //                     << "   client say: " << buffer;

                    std::string echo_str = "Translate: "; // 我们要给客户端回显一条消息
                    echo_str += _findword(std::string(buffer)); // 调用传入的函数来获取翻译结果

                    // 发送回显消息
                    ssize_t m = ::sendto(_sockfd, echo_str.c_str(), echo_str.size(), 0, (struct sockaddr *)&peer, len);
                    if (m < 0)
                    {
                        LOG(LogLevel::ERROR) << "sendto error: " << strerror(errno);
                    }
                }
            }
        }

    private:
        int _sockfd;    // socket文件描述符
        InetAddr local; // 本地地址信息
        // std::string _ip; // 默认IP地址
        // uint16_t _port;  // 默认端口号

        bool is_running; // 服务器是否在运行

        find_t _findword;
    };
}

#endif